
import uuid from './uuid'
import storage from '../utils/storage'
import { getErrorMessage } from './ResponseError'

const timeoutErrorMessage = '连接服务器超时',
  HTTP_ERROR = {
    'Network Error': '请求服务端失败'
  }

const LOST_SESSION_CODE = [1200, 1202, 1203, 1204, 1205, 1206, 1207]

// 存储前端发送请求的数组
const subscribesArr = []
// 当标志为true 代表程序正在获取新token
let exchangingToken = false

// 被拦截的请求push到数组中
function subscribesArrRefresh (cb) {
  subscribesArr.push(cb)
}
// 用新token发起请求队列中的所有请求
function reloadSubscribesArr (newToken) {
  subscribesArr.map(cb => {
    cb(newToken)
  })
}

/**
 *  注册拦截器，扩展 axios 属性
 * @param axios
 */
export function initialize (axios) {
  // 拦截请求
  registRequestInterceptor(axios)

  // 拦截响应
  registResponseInterceptor(axios)

  // 扩展属性
  extendAttributes(axios)

  /**
   * 扩展文件下载接口
   */
  axios.download = (url, config = {}) => {
    config.responseType = config.responseType || 'blob'
    return new Promise((resolve, reject) => {
      axios.get(url, config).then(response => {
        if (response.data instanceof ArrayBuffer) {
          resolve(response.data)
        }
        if (response?.data?.type === 'text/xml') {
          resolve(response.data)
        } else {
          let reader = new FileReader()
          reader.readAsText(response.data, 'utf-8')
          reader.onload = function (e) {
            response.data = JSON.parse(reader.result)
            handleResponse(response, axios)
          }
        }
      }).catch(e => {
        reject(e)
      })
    })
  }
}

function registRequestInterceptor (axios) {
  axios.interceptors.request.use((config) => {
    config.url = `${config.url}${config.url.includes('?') ? '&' : '?'}timestamp=${new Date().getTime()}`.replace(/\/+/g, '/').replace(':/', '://')
    config.timeoutErrorMessage = timeoutErrorMessage
    if (config.headers) {
      const key = Object.keys(config.headers).find(key => key.toLocaleLowerCase() == 'content-type')
      !key && (config.headers['Content-Type'] = 'application/json;charset=UTF-8')
    } else {
      config.headers['Content-Type'] = 'application/json;charset=UTF-8'
    }

    let ukey = axios.getUkey()
    config.headers.ukey = ukey

    if (config.method.toLowerCase() === 'get') {
      config.params = config.params || config.data
      delete config.data
    } else if (config.method.toLowerCase() === 'post') {
      config.data = config.data || config.params
      delete config.params
    }

    const encryptor = axios.getEncryptor()
    if (config.needEncrypt !== false && encryptor && config.data) {
      // 加密传输
      config.data = encryptor.encrypt(JSON.stringify(config.data))
    }

    if (config.needToken === false) {
      axios.beforeLoad(config)
      return config
    }

    // 设置请求头 token
    const accessToken = axios.getToken()
    if (!accessToken) {
      const e = new Error()
      e.message = '登录信息失效'
      e.response = { data: { code: 1200 } }
      e.config = config
      e.request = config.request
      e.rejected = true
      throw e
    }
    if (accessToken.expired > 0) {
      config.headers['Access-Token'] = accessToken.value
      config.headers.token = accessToken.value
      axios.beforeLoad(config)
      return config
    }
    if (!axios.exchangeToken) {
      return config
    }
    if (!exchangingToken) {
      // 换取token
      exchangingToken = true
      axios.exchangeToken(accessToken.value).then(response => {
        exchangingToken = false
        storage.setToken(response.token, response.validTime)
        reloadSubscribesArr(response.token)
      }).catch(e => {
        axios.onError && axios.onError(e)
      })
    }
    return new Promise((resolve, reject) => {
      subscribesArrRefresh((newToken) => {
        config.headers.token = newToken
        config.headers['Access-Token'] = newToken
        resolve(config)
      })
    })
  }, e => {
    return Promise.reject(e)
  })
}

function registResponseInterceptor (axios) {
  axios.interceptors.response.use((response) => {
    axios.afterLoad(response)

    if (response.headers['content-type'] && response.headers['content-type'].startsWith('image')) {
      // 图片转base64
      return 'data:image/png;base64,' + btoa(new Uint8Array(response.data).reduce((data, byte) => data + String.fromCharCode(byte), ''))
    }
    if (response.data instanceof Blob || response.data instanceof ArrayBuffer) {
      // 文件下载不做处理
      return response
    }
    return handleResponse(response, axios)
  }, e => {
    if (e.response) {
      const mesage = getErrorMessage(e.response)
      mesage && (e.message = mesage)
    } else {
      e.message =
        HTTP_ERROR[e.message] ||
        (e.response && e.response.data && e.response.data.msg) ||
        (e.response && e.response.data && e.response.data.message) ||
        e.message
    }
    e.response && (e.response = e.response.data)
    if (e.response?.status == 401 || LOST_SESSION_CODE.includes(e.response?.code)) {
      axios.onLostSession(e)
    } else {
      axios.onError(e)
    }
    return Promise.reject(e)
  })
}

/**
 * 处理 response 结果
 * @param {*} response
 * @param {*} axios
 * @returns
 */
function handleResponse (response, axios) {
  if (response.data.code === 0) {
    const encryptor = axios.encryptor
    if (response.config.needEncrypt !== false && encryptor && response.data.data) {
      return JSON.parse(encryptor.decrypt(response.data.data))
    } else {
      return response.data.data
    }
  }
  const e = new Error()
  e.message = response.data.msg || response.data.message
  e.response = response.data
  e.config = response.config
  e.request = response.request
  e.rejected = true
  if (response.config.handleError !== false) {
    if (e.response?.status == 401 || LOST_SESSION_CODE.includes(e.response?.code)) {
      axios.onLostSession(e)
    } else {
      // 如请求中指定不拦截失败的请求，则全局拦截不处理，由具体发起http处自行处理
      axios.onError(e)
    }
  }
  return Promise.reject(e)
}

function extendAttributes (axios) {
  Object.assign(axios, {
    addRequestInterceptor (interceptor) {
      axios.interceptors.request.use(interceptor)
    },
    addResponseInterceptor (interceptor) {
      axios.interceptors.response.use(interceptor)
    },
    getToken () {
      return storage.getToken()
    },
    getUkey () {
      let ukey = storage.get('__ukey__')
      if (!ukey) {
        ukey = uuid(8, 16)
        storage.set('__ukey__', ukey)
      }
      return ukey
    },
    onLostSession: e => { },
    onError: e => { },
    beforeLoad: config => { },
    afterLoad: response => { },
    getEncryptor: () => {
      return axios.encryptor
    },
    setEncryptor: (encryptor) => {
      axios.encryptor = encryptor
    },
    exchangeToken: null
  })
}